(() => {
  //------- js基础第三天 案例部分 封装为函数 并加上类型定义 -------
  // 1.倒三角
  // function printTriangle(n: number): void {
  //    for(let i:number = 1; i <= n; i++){
  //     for(let j:number = 1; j <= i; j++){
  //       document.write("★");
  //     }
  //     document.write("<br>");
  //    }
  // }
  /**
   * for...of案例
   * 1. for...of 语句的左侧不能使用类型批注，只能使用let或const进行声明
   * 2. for...of的遍历对象必须具有Symbol.iterator属性，即该对象必须是可迭代对象
   * 3. for...of的遍历对象可以是数组、Map、Set、字符串、TypedArray、arguments对象等
   */
  // 案例 1：过滤数组中的奇数并输出
  const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  function removeOdds(numbers: number[]): number[] {
    const result: number[] = [];
    for (const num of numbers) {
      if (num % 2 === 0) {
        result.push(num);
      }
    }
    return result;
  }
  console.log(removeOdds(numbers));

  // 案例 2：查找并输出字符串数组中包含特定子字符串的元素
  const phrases: string[] = ['hello world', 'javascript is fun', 'openai gpt', 'coding is great'];
  function findSubString(phrases: string[], subString: string): string[] {
    const result: string[] = []
    for (const phrase of phrases) {
      if (phrase.includes('is')) {
        result.push(phrase);
      }
    }
    return result
  }
  console.log(findSubString(phrases, 'is'));

  // 案例 3：计算并输出包含负数的数组的正数总和
  const mixedNumbers: number[] = [-3, 5, -1, 8, -2, 7];
  function sumPositive(mixedNumbers: number[]): number {
    let sum: number = 0;
    for (const num of mixedNumbers) {
      if (num > 0) {
        sum += num;
      }
    }
    console.log(`Total sum of positive numbers: ${sum}`);
    return sum
  }
  sumPositive(mixedNumbers);

  /**
   * for...in 案例
   * 1. for...in 语句的左侧不能使用类型批注，只能使用let或const进行声明
   * 2. for...in 语句的右侧可以是对象、数组、Map、Set、字符串、TypedArray、arguments对象等
   * 3. for...in 语句可以遍历对象的属性，也可以遍历数组的索引
   */
  // 案例 1：遍历对象属性并输出符合条件的属性值的属性
  //可以使用接口interface或类型type定义一个具有特定结构的接口
  interface IPerson {
    name: string
    age: number
    city: string
    occupation: string
    // 方法一：索引签名定义键值类型（推荐）
    // [index: string]: string | number
  }
  const person: IPerson = {
    name: 'Alice',
    age: 25,
    city: 'Wonderland',
    occupation: 'Engineer'
  };
  function printFitProp(person: IPerson): string[] {
    const result: Array<string> = [];
    for (const key in person) {
      // 方法二：使用类型断言，确定遍历键值对的类型
      /**
       * key as keyof Person 类型断言
       * 1. keyof 是 TypeScript 中的一个关键字，用于获取对象类型的所有键（属性名）的联合类型。
       * 2. keyof 的一个常见用途是在泛型函数中，以确保类型安全地访问对象的属性
       */
      if (person[key as keyof IPerson] === 'Engineer') {
        result.push(key);
      }
    }
    return result;
  }
  console.log(printFitProp(person));
  // 方法三：使用Object.entries()方法获取对象所有属性名，再遍历
  for (const [key, value] of Object.entries(person)) {
    if (value === 'Engineer') {
      console.log(`${key}: ${value}`);
    }

    // 案例 2：计算对象中数值属性的总和
    interface IData {
      [index: string]: string | number
    }
    const data: IData = {
      a: 10,
      b: 20,
      c: 'thirty',
      d: 40,
      e: 'fifty'
    };
    function sumNumValues(data: IData): number {
      let sum: number = 0;
      for (const key in data) {
        if (typeof data[key] === 'number') {
          sum += data[key];
        }
      }
      console.log(`Total sum of numeric values: ${sum}`);
      return sum
    }

    // 案例 3：查找对象中符合条件的属性并输出键和值
    interface IFruits {
      [index: string]: number
    }
    const inventory: IFruits = {
      apples: 5,
      oranges: 0,
      bananas: 8,
      grapes: 2,
      cherries: 0
    };
    function findFitArray(inventory: IFruits, prop: string): string[] {
      const result: string[] = []
      for (const key in inventory) {
        if (inventory[key] === 0) {
          result.push(key)
          console.log(`${key}: ${inventory[key]}`);
        }

      }
      return result.map(key => `${key}: ${inventory[key]}`)
    }
    console.log(findFitArray(inventory, 'cherries'))

    /**
     * forEach案例
     * 
     */

    // 案例 1：查找并输出数组中的正数并计算它们的平均值
    const numbers1: number[] = [-3, 5, -1, 8, -2, 7];

    function findPositiveAndAve(numbers: number[]): number {
      let sum: number = 0;
      let count: number = 0;

      numbers.forEach(num => {
        if (num > 0) {
          sum += num;
          count++;
        }
      });

      const average: number = sum / count;
      console.log(`Average of positive numbers: ${average}`);
      return average
    }

    findPositiveAndAve(numbers1);

    // 案例 2：查找数组中的字符串长度并输出超过特定长度的字符串
    const words: string[] = ['apple', 'banana', 'cherry', 'date', 'fig', 'grape'];
    const minLength: number = 5;
    function findLongWords(words: string[], minLength: number): string[] {
      const result: string[] = [];
      words.forEach(word => {
        if (word.length > minLength) {
          result.push(word)
        }
      });
      return result
    }
    console.log(findLongWords(words, minLength));

    /**
     * map案例
     */

    // 案例 1：将数组中的负数转换为正数
    const numbers2: number[] = [-3, 5, -1, 8, -2, 7];

    function convertToPositive(numbers: number[]): number[] {
      const positiveNumbers: number[] = numbers.map(num => {
        return num < 0 ? -num : num;
      });
      return positiveNumbers
    }
    console.log(convertToPositive(numbers2));

    // 案例 2：将对象数组中的特定属性值大写化
    interface IUserInfos {
      // 此处不能用index签名，因为对象数组的属性名不确定，导致无法用length方法（number无length方法）
      // [index: string]: string | number
      name: string
      age: number

    }

    const people: IUserInfos[] = [
      { name: 'Alice', age: 25 },
      { name: 'Bob', age: 30 },
      { name: 'Charlie', age: 35 }
    ];

    function capticalIsNames(people: IUserInfos[]): IUserInfos[] {
      const uppercasedNames: IUserInfos[] = people.map(person => {
        return person.name.length > 3 ? { ...person, name: person.name.toUpperCase() } : person;
      });

      return uppercasedNames;
    }

    console.log(capticalIsNames(people));

    // 案例 3：将字符串数组中的特定子字符串替换为另一字符串
    const phrases1: string[] = ['hello world', 'javascript is fun', 'hello openai', 'coding is great'];

    function replaceSubString(phrases: string[], subString: string, newSubString: string): string[] {
      const updatedPhrases: string[] = phrases.map(phrase => {
        return phrase.includes('hello') ? phrase.replace('hello', 'hi') : phrase;
      });
      return updatedPhrases;
    }

    console.log(replaceSubString(phrases1, 'hello', 'hi'));

    /**
     * 使用 map、filter 和 reduce 方法结合的案例
     */

    // 案例 1：计算并输出长度大于 5 的字符串的总长度
    const phrases2: string[] = ['hello world', 'javascript', 'openai', 'coding is fun'];
    function sumLongStrings(phrases: string[]): number {
      const totalLength = phrases
        .filter(phrase => phrase.length > 5) // 过滤长度大于5的字符串
        .map(phrase => phrase.length)        // 将每个字符串转换为其长度
        .reduce((sum, length) => sum + length, 0); // 计算总长度

      console.log(`Total length of strings longer than 5 characters: ${totalLength}`);
      return totalLength;
    }
    console.log(sumLongStrings(phrases2));

    // 案例 2：计算正数数组的平方和，并排除负数
    const numbers3: number[] = [-5, 3, -2, 4, 6, -1];

    function sumPositiveSquares(numbers: number[]): number {
      const squareSum = numbers
        .filter(num => num > 0)        // 过滤正数
        .map(num => num * num)         // 将每个正数平方
        .reduce((sum, squared) => sum + squared, 0); // 计算平方和
      console.log(`Sum of squares of positive numbers: ${squareSum}`);
      return squareSum;
    }
    console.log(sumPositiveSquares(numbers3));

    // 案例 3：处理用户数据，提取年龄大于 18 的用户，并计算他们的平均年龄
    interface IUsers {
      name: string
      age: number
    }
    const users: IUsers[] = [
      { name: 'Alice', age: 22 },
      { name: 'Bob', age: 17 },
      { name: 'Charlie', age: 30 },
      { name: 'David', age: 15 }
    ];
    function filterAndAveAge(users: IUsers[]): number {
      const averageAge = users
        .filter(user => user.age > 18) // 过滤年龄大于18的用户
        .map(user => user.age)         // 提取用户年龄
        .reduce((sum, age, _, array) => sum + age / array.length, 0); // 计算平均年龄
      console.log(`Average age of users older than 18: ${averageAge}`);
      return averageAge;
    }
    console.log(filterAndAveAge(users));

    /**
     * 使用 findIndex、splice 方法结合案例
     */

    // 案例 1：在购物车中移除指定商品
    interface ICartInfos {
      id: number
      name: string
      quantity: number
    }
    const cart: ICartInfos[] = [
      { id: 1, name: 'Laptop', quantity: 1 },
      { id: 2, name: 'Mouse', quantity: 2 },
      { id: 3, name: 'Keyboard', quantity: 1 }
    ];
    function removeItem(cart: ICartInfos[], id: number): ICartInfos[] {
      const itemIdToRemove: number = 2;
      const indexToRemove: number = cart.findIndex(item => item.id === itemIdToRemove);

      if (indexToRemove !== -1) {
        cart.splice(indexToRemove, 1);
      }
      return cart;
    }
    console.log(removeItem(cart, 2));

    // 案例 2：更新数组中的指定对象属性
    interface IEmployeesInfos {
      id: number
      name: string
      role: string
    }
    const employees: IEmployeesInfos[] = [
      { id: 1, name: 'John', role: 'Developer' },
      { id: 2, name: 'Jane', role: 'Designer' },
      { id: 3, name: 'Tom', role: 'Manager' }
    ];
    const employeeIdToUpdate: number = 3;
    const newRole: string = 'Senior Manager';

    function updateRole(employees: IEmployeesInfos[], id: number, newRole: string): IEmployeesInfos[] {
      const indexToUpdate: number = employees.findIndex(employee => employee.id === employeeIdToUpdate);

      if (indexToUpdate !== -1) {
        employees[indexToUpdate].role = newRole;
      }

      return employees;
    }
    console.log(updateRole(employees, employeeIdToUpdate, newRole));

    // 案例 3：将数组中的第一个负数替换为零
    const numbers4: number[] = [4, -1, 6, -2, 7];

    function replaceFirstNegative(numbers: number[]): number[] {
      const indexToReplace: number = numbers.findIndex(num => num < 0);
      if (indexToReplace !== -1) {
        numbers.splice(indexToReplace, 1, 0);
      }
      return numbers;
    }
    console.log(replaceFirstNegative(numbers4));
  }
})()
